package com.gtw.prototype;

import java.io.*;
import java.util.ArrayList;

/**
 * 通过复制一个实例对象本身来创建一个新的实例,通过原型实例创建新的对象，就不再需要关心这个实例本身的类型
 * 注意clone事项:
 *      1.clone方法来完成的，它直接在内存中复制数据，因此不会调用到类的构造方法。不但构造方法中的代码不会执行，甚至连访问权限都对原型模式无效
 *      2.clone方法只会拷贝对象中的基本的数据类型(包括String)。对于数组、容器对象、引用对象等都不会拷贝，还是指向原生对象的内部元素地址,这就是浅拷贝。
 *        如果要实现深拷贝，必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
 *      3.Java语言里深度克隆一个对象，常常可以先使对象实现Serializable接口
 * 最佳实践:
 *      一个对象的产生可以不由零起步,可以直接从一个已经具备雏形的对象clone,然后再修改成生成所需的对象
 */
public class Prototype implements Cloneable, Serializable {

    private String name;

    /**
     * 要使用clone方法,成员变量上不要加final字段
     */
    private ArrayList list = new ArrayList();

    public Prototype() {
        System.out.println("clone出来的方法不会调用到此构造方法");
    }

    /**
     * 深拷贝和浅拷贝建议不要混合使用,涉及到类的继承时,父类有多个引用会比较复杂,建议深拷贝和浅拷贝分开实现
     */
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        Prototype prototype = null;
        try {
            prototype = (Prototype) super.clone();
//            this.list = (ArrayList) this.list.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return prototype;
    }

    /**
     * 写到流里的是对象的一个拷贝，而原对象仍然存在于JVM里面。
     */
    public Object deepClone() throws IOException, ClassNotFoundException{
        //将对象写到流里
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //从流里读回来
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setList(String value) {
        list.add(value);
    }

    public ArrayList getList() {
        return this.list;
    }
}
